#include "lexic_analiser.h"

static string a[14] = { "int", "string", "bool", "function", "void", "if", "else", "while", "print",
		"echo", "read", "return", "if", "else" };
static set<string> reservadas(a, a + 14);

static char b[8] = { '[', ']', '{', '}', '(', ')', ';', ',' };
static set<char> simbolos(b, b + 8);

static char c[11] = { '+', '-', '/', '*', '=', '<', '>', '!', '|', '&', '@' };
static set<char> operadores(c, c + 11);

int chamada;

vector<Chamada> matriz;

bool eh_digito(char c);

namespace myiostream {
int linha = 1;
stack<char> stream;

char ler_simbolo_sem_espaco();
char ler_simbolo();
void retornar_simbolo(char c);

char ler_simbolo_sem_espaco() {
	char c = ler_simbolo();

	while (c == '\n' || c == ' ' || c == '\t')
		c = ler_simbolo();

	return (c);
}

char ler_simbolo() {
	char c;

	if (!stream.empty()) {
		c = stream.top();
		stream.pop();
	} else {
		c = getchar();
	}

	if (c == '\n') {
		linha++;
	}
	return (c);

}

void retornar_simbolo(char c) {
	stream.push(c);
}

}

bool eh_alphanumeric(char c) {
	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c
			<= '9') || (c == '_'))
		return (true);
	return (false);
}

void ler_identificador() {
	string ident;
	char c;

	ident += myiostream::ler_simbolo();

	while ((c = myiostream::ler_simbolo()) != EOF) {
		if (eh_alphanumeric(c) || c == '_') {
			ident += c;
		} else {
			myiostream::retornar_simbolo(c);
			break;
		}
	}

	if (ident.size() > LIM_MAX_IDENT) {
		Chamada d;
		d.chamada = ++chamada;
		d.cadeia = ident.substr(0, LIM_MAX_IDENT);
		d.simbolo = "IDENT";
		d.codigo = IDENTIFICADOR;
		d.linha = myiostream::linha;

		matriz.push_back(d);
		throw string(
				"erro:Tamanho do Identificador Invalido, maior do que permitido.");
	}

	if (ident.size() < LIM_MIN_IDENT) {
		Chamada d;
		d.chamada = ++chamada;
		d.cadeia = ident;
		d.simbolo = "IDENT";
		d.codigo = IDENTIFICADOR;
		d.linha = myiostream::linha;

		matriz.push_back(d);
		throw string("erro:Falta nome do Identificador.");

	}

	Chamada d;
	d.chamada = ++chamada;
	d.cadeia = ident;
	d.simbolo = "IDENT";
	d.codigo = IDENTIFICADOR;
	d.linha = myiostream::linha;

	if(eh_digito(ident[0])){
		d.codigo = SIMBOLO_INVALIDO;
	}

	matriz.push_back(d);
}

void ler_digito() {
	string numero;
	char c;

	while ((c = myiostream::ler_simbolo()) != EOF) {
		if (eh_digito(c)) {
			numero += c;
		} else {
			myiostream::retornar_simbolo(c);
			break;
		}
	}

	if (eh_alphanumeric(c)) {
		for (int i = numero.size() - 1; i >= 0; i--) {
			myiostream::retornar_simbolo(numero[i]);
		}
		ler_identificador();
		return;
	}

	Chamada d;
	d.chamada = ++chamada;
	d.cadeia = numero;
	d.simbolo = "NUMERO";
	d.codigo = NUMERO;
	d.linha = myiostream::linha;

	matriz.push_back(d);
}

void ler_palavra_reservada_ou_funcao() {
	string reservada;
	char c;

	while ((c = myiostream::ler_simbolo()) != EOF) {
		if (eh_alphanumeric(c)) {
			reservada += c;
		} else {
			myiostream::retornar_simbolo(c);
			break;
		}
	}

	Chamada d;
	d.chamada = ++chamada;
	d.cadeia = reservada;
	d.simbolo = reservada;
	d.linha = myiostream::linha;

	if( d.cadeia == "function" ){
		d.codigo = DECLARACAO_FUNCAO;
	}else if( d.cadeia == "int" || d.cadeia == "string" || d.cadeia == "void" || d.cadeia == "bool" ){
		d.codigo = NOME_DE_TIPO;
	}else if (!binary_search(reservadas.begin(), reservadas.end(), reservada)) {
		d.codigo = IDENTIFICADOR_DE_FUNCAO;
	} else {
		d.codigo = PALAVRA_RESERVADA;
	}
	matriz.push_back(d);
}

bool eh_digito(char c) {
	if (c >= '0' && c <= '9')
		return (true);
	return (false);
}

bool eh_identificador(char c) {
	if (c == '$')
		return (true);
	return (false);
}

bool eh_alpha(char c) {
	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
		return (true);
	return (false);
}

void ler_simbolo() {
	char c = myiostream::ler_simbolo();
	string s;
	s += c;

	Chamada d;
	d.chamada = ++chamada;
	d.cadeia = s;
	d.simbolo = s;
	d.linha = myiostream::linha;
	d.codigo = 0;

	if (simbolos.find(c) != simbolos.end()) {
		switch (c) {
		case '(':
			d.codigo = ABRE_PARENTESE;
			break;
		case ')':
			d.codigo = FECHA_PARENTESE;
			break;
		case '[':
			d.codigo = ABRE_COLCHETE;
			break;
		case ']':
			d.codigo = FECHA_COLCHETE;
			break;
		case '{':
			d.codigo = ABRE_CHAVE;
			break;
		case '}':
			d.codigo = FECHA_CHAVE;
			break;
		case ';':
			d.codigo = PONTO_E_VIRGULA;
			break;
		case ',':
			d.codigo = VIRGULA;
			break;
		default:
			matriz.push_back(d);
			throw string(string("erro:Simbolo ") + c + string(
					" nao permitido na linguagem."));
			break;
		}

	} else if (operadores.find(c) != operadores.end()) {
		char aux;

		switch (c) {
		case '@':
			d.codigo = CONCATENACAO;
			break;
		case '+':
			d.codigo = SOMA;
			break;
		case '-':
			d.codigo = SUBTRACAO;
			break;
		case '/':
			d.codigo = DIVISAO;
			break;
		case '*':
			d.codigo = MULTIPLICACAO;
			break;
		case '=':
			if ((c = myiostream::ler_simbolo()) == '=') {
				d.codigo = IGUALDADE;
				d.cadeia += c;
				d.simbolo += c;
			} else {
				d.codigo = ATRIBUICAO;
				myiostream::retornar_simbolo(c);
			}
			break;
		case '<':
			if ((aux = myiostream::ler_simbolo()) == '=') {
				d.codigo = MENOR_IGUAL;
				d.cadeia += aux;
				d.simbolo += aux;
			} else {
				d.codigo = MENOR;
				myiostream::retornar_simbolo(aux);
			}
			break;
		case '>':
			if ((aux = myiostream::ler_simbolo()) == '=') {
				d.codigo = MAIOR_IGUAL;
				d.cadeia += aux;
				d.simbolo += aux;
			} else {
				d.codigo = MAIOR;
				myiostream::retornar_simbolo(aux);
			}
			break;
		case '!':
			if ((aux = myiostream::ler_simbolo()) == '=') {
				d.codigo = DIFERENCA;
				d.cadeia += aux;
				d.simbolo += aux;
			} else {
				myiostream::retornar_simbolo(aux);
				return;
			}
			break;
		case '|':
			if ((aux = myiostream::ler_simbolo()) == '|') {
				d.codigo = OU_BOOLEANO;
				d.cadeia += aux;
				d.simbolo += aux;
			} else {
				myiostream::retornar_simbolo(aux);
				return;
			}
			break;
		case '&':
			if ((aux = myiostream::ler_simbolo()) == '&') {
				d.codigo = E_BOOLEANO;
				d.cadeia += aux;
				d.simbolo += aux;
			} else {
				myiostream::retornar_simbolo(aux);
				return;
			}
			break;
		default:
			matriz.push_back(d);
			throw string(string("erro:Simbolo ") + c + string(
					" nao permitido na linguagem."));
			break;
		}
	} else {
		matriz.push_back(d);
		throw string(string("erro:Simbolo ") + c + string(
				" nao permitido na linguagem."));
	}

	matriz.push_back(d);
}

void ler_string() {
	string str;
	char c, first = myiostream::ler_simbolo();

	try {
		while (true) {
			c = myiostream::ler_simbolo();
			if (c == EOF) {
				throw string("erro: Esperado \' ou \".");
			}

			if (c != first || (c == first && str[str.size() - 1] == '\\')) {
				str += c;
			} else {
				break;
			}
		}
	} catch (string str_exeception) {
		Chamada d;
		d.chamada = ++chamada;
		d.cadeia = str;
		d.simbolo = "STRING";
		d.codigo = STRING;
		d.linha = myiostream::linha;

		matriz.push_back(d);
		throw str_exeception;
	}

	Chamada d;
	d.chamada = ++chamada;
	d.cadeia = str;
	d.simbolo = "STRING";
	d.codigo = STRING;
	d.linha = myiostream::linha;

	matriz.push_back(d);
}

vector<Chamada> get_lexic_analiser() {
	char c;
	chamada = 0;

	try {
		while ((c = myiostream::ler_simbolo_sem_espaco()) != EOF) {
			myiostream::retornar_simbolo(c);

			if (eh_identificador(c)) {
				ler_identificador();
				continue;
			}
			if (eh_digito(c)) {
				ler_digito();
				continue;
			}
			if (eh_alpha(c)) {
				ler_palavra_reservada_ou_funcao();
				continue;
			}

			if (c == '"' || c == '\'') {
				ler_string();
				continue;
			}

			ler_simbolo();
		}
	} catch (string str) {
		char n[5];
		itoa(matriz.back().linha, n, 10);
		throw string(string(n) + ": " + str);
	}

	return (matriz);
}
